package cn.zl.GB28181.storage.impl;

import cn.yunbamboos.bean.ThreadPoolDynamicTaskExecutor;
import cn.zl.GB28181.cache.DeviceCache;
import cn.zl.GB28181.core.SipTransactionInfo;
import cn.zl.GB28181.core.SyncStatus;
import cn.zl.GB28181.storage.IDeviceStorage;
import cn.zl.GB28181.transmit.cmd.ISIPCommander;
import cn.zl.GB28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
import cn.zl.GB28181.utils.DateUtils;
import cn.zl.common.CommonCallback;
import cn.zl.common.ZLConst;
import cn.zl.config.UserSettingConfig;
import cn.zl.mapper.DeviceChannelMapper;
import cn.zl.mapper.DeviceMapper;
import cn.zl.model.Device;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import java.text.ParseException;

@Component
public class DeviceStorageImpl implements IDeviceStorage {

    private static final Logger logger = LoggerFactory.getLogger(DeviceStorageImpl.class);

    @Resource
    private DeviceCache deviceCache;
    @Resource
    private DeviceMapper deviceMapper;
    @Resource
    private DeviceChannelMapper deviceChannelMapper;
    @Resource(name = "DynamicTaskExecutor")
    private ThreadPoolDynamicTaskExecutor dynamicTask;
    @Resource
    private UserSettingConfig userSettingConfig;
    @Resource
    private ISIPCommander sipCommander;
    @Resource
    private CatalogResponseMessageHandler catalogResponseMessageHandler;

    @Override
    public Device getDevice(String deviceId) {
        Device device = deviceCache.get(deviceId);
        if (device == null) {
            device = deviceMapper.queryByDeviceId(deviceId);
            if (device != null) {
                deviceCache.add(device);
            }
        }
        return device;
    }

    @Override
    public void updateDevice(Device device) {
        String now = DateUtils.getNow();
        device.setUpdateTime(now);
        device.setCharset(device.getCharset().toUpperCase());
        device.setUpdateTime(DateUtils.getNow());
        if (deviceMapper.update(device) > 0) {
            deviceCache.update(device);
        }
    }

    @Override
    public void online(Device device, SipTransactionInfo sipTransactionInfo) {
        logger.info("[设备上线] deviceId：{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
        Device deviceInCache = deviceCache.get(device.getDeviceId());
        Device deviceInDb = deviceMapper.queryByDeviceId(device.getDeviceId());

        String now = DateUtils.getNow();
        if (deviceInCache != null && deviceInDb == null) {
            // 内存中存在脏数据
            deviceCache.clear(device.getDeviceId());
        }
        device.setUpdateTime(now);
        device.setKeepaliveTime(now);

        if (device.getKeepaliveIntervalTime() == 0) {
            // 默认心跳间隔60
            device.setKeepaliveIntervalTime(60);
        }
        if (sipTransactionInfo != null) {
            device.setSipTransactionInfo(sipTransactionInfo);
        } else {
            if (deviceInCache != null) {
                device.setSipTransactionInfo(deviceInCache.getSipTransactionInfo());
            }
        }

        // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询
        if (deviceInDb == null) {
            device.setOnLine(true);
            device.setCreateTime(now);
            device.setUpdateTime(now);
            logger.info("[设备上线,首次注册]: {}，查询设备信息以及通道信息", device.getDeviceId());
            deviceMapper.insert(device);
            deviceCache.add(device);
            try {
                sipCommander.deviceInfoQuery(device);
            } catch (InvalidArgumentException | SipException | ParseException e) {
                logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
            }
            sync(device);
        } else {
            if (!device.isOnLine()) {
                device.setOnLine(true);
                device.setCreateTime(now);
                deviceMapper.update(device);
                deviceCache.add(device);
                if (userSettingConfig.isSyncChannelOnDeviceOnline()) {
                    logger.info("[设备上线,离线状态下重新注册]: {}，查询设备信息以及通道信息", device.getDeviceId());
                    try {
                        sipCommander.deviceInfoQuery(device);
                    } catch (InvalidArgumentException | SipException | ParseException e) {
                        logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage());
                    }
                    sync(device);
                    // TODO 如果设备下的通道级联到了其他平台，那么需要发送事件或者notify给上级平台
                }
            } else {
                if (deviceChannelMapper.queryAllChannelsByDeviceId(device.getDeviceId()).isEmpty()) {
                    logger.info("[设备上线]: {}，通道数为0,查询通道信息", device.getDeviceId());
                    sync(device);
                }
                deviceMapper.update(device);
                deviceCache.add(device);
            }
        }
        // 刷新过期任务
        String registerExpireTaskKey = ZLConst.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
        // 如果第一次注册那么必须在60 * 3时间内收到一个心跳，否则设备离线
        dynamicTask.startDelay(registerExpireTaskKey, () -> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
    }

    @Override
    public void offline(String deviceId, String reason) {
        logger.warn("[设备离线]，{}, device：{}", reason, deviceId);
        Device device = deviceMapper.queryByDeviceId(deviceId);
        if (device == null) {
            return;
        }
        String registerExpireTaskKey = ZLConst.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId;
        dynamicTask.stop(registerExpireTaskKey);
        device.setOnLine(false);
        deviceCache.update(device);
        deviceMapper.update(device);
        // 更新设备通道状态
        deviceChannelMapper.updateStatusByDeviceId(device.getDeviceId(), false);
        // 离线释放所有ssrc
        // 移除订阅
    }

    @Override
    public boolean addCatalogSubscribe(Device device) {
        if (device == null || device.getSubscribeCycleForCatalog() < 0) {
            return false;
        }
        logger.info("[添加目录订阅] 设备{}", device.getDeviceId());
        return true;
    }

    @Override
    public boolean removeCatalogSubscribe(Device device, CommonCallback<Boolean> callback) {
        if (device == null || device.getSubscribeCycleForCatalog() < 0) {
            return false;
        }
        logger.info("[移除目录订阅]: {}", device.getDeviceId());
        return true;
    }

    @Override
    public SyncStatus getChannelSyncStatus(String deviceId) {
        return catalogResponseMessageHandler.getChannelSyncProgress(deviceId);
    }

    @Override
    public Boolean isSyncRunning(String deviceId) {
        return catalogResponseMessageHandler.isSyncRunning(deviceId);
    }

    @Override
    public void sync(Device device) {
        if (catalogResponseMessageHandler.isSyncRunning(device.getDeviceId())) {
            logger.info("开启同步时发现同步已经存在");
            return;
        }
        int sn = (int) ((Math.random() * 9 + 1) * 100000);
        catalogResponseMessageHandler.setChannelSyncReady(device, sn);
        try {
            sipCommander.catalogQuery(device, sn, event -> {
                String errorMsg = String.format("同步通道失败，错误码： %s, %s", event.statusCode, event.msg);
                catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg);
            });
        } catch (SipException | InvalidArgumentException | ParseException e) {
            logger.error("[同步通道], 信令发送失败：{}", e.getMessage());
            String errorMsg = String.format("同步通道失败，信令发送失败： %s", e.getMessage());
            catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg);
        }
    }

}
